package action_queue

import (
	"runtime/debug"
	"sync"
)

// 不限制大小，添加不发生阻塞，接收阻塞等待
type ActionPipe struct {
	list      []func()
	listGuard sync.Mutex
	listCond  *sync.Cond
}

// 添加时不会发送阻塞
func (ap *ActionPipe) Add(action func()) {
	ap.listGuard.Lock()
	ap.list = append(ap.list, action)
	ap.listGuard.Unlock()

	ap.listCond.Signal()
}

func (ap *ActionPipe) Reset() {
	ap.list = ap.list[0:0]
}

// 如果没有数据，发生阻塞
func (ap *ActionPipe) Pick(retList *[]func()) (exit bool) {

	ap.listGuard.Lock()

	for len(ap.list) == 0 {
		ap.listCond.Wait()
	}

	ap.listGuard.Unlock()

	ap.listGuard.Lock()

	// 复制出队列

	for _, data := range ap.list {

		if data == nil {
			exit = true
			break
		} else {
			*retList = append(*retList, data)
		}
	}

	ap.Reset()
	ap.listGuard.Unlock()

	return
}

// 事件队列
type ActionQueue interface {
	// 事件队列开始工作
	StartLoop() ActionQueue

	// 停止事件队列
	StopLoop() ActionQueue

	// 等待退出
	Wait()

	// 投递事件, 通过队列到达消费者端
	Post(action func())

	// 是否捕获异常
	EnableCapturePanic(v bool)
}

func NewActionPipe() *ActionPipe {
	self := &ActionPipe{}
	self.listCond = sync.NewCond(&self.listGuard)
	return self
}

type actionQueue struct {
	*ActionPipe
	endSignal    sync.WaitGroup
	capturePanic bool
}

// 启动崩溃捕获
func (aq *actionQueue) EnableCapturePanic(v bool) {
	aq.capturePanic = v
}

// 派发事件处理回调到队列中
func (aq *actionQueue) Post(callback func()) {

	if callback == nil {
		return
	}

	aq.Add(callback)
}

// 保护调用用户函数
func (aq *actionQueue) protectedCall(callback func()) {

	if aq.capturePanic {
		defer func() {

			if err := recover(); err != nil {
				debug.PrintStack()
			}
		}()
	}

	callback()
}

// 开启事件循环
func (aq *actionQueue) StartLoop() ActionQueue {

	aq.endSignal.Add(1)

	go func() {

		var writeList []func()

		for {
			writeList = writeList[0:0]
			exit := aq.Pick(&writeList)

			// 遍历要发送的数据
			for _, action := range writeList {
				if action != nil {
					aq.protectedCall(action)
				}
			}

			if exit {
				break
			}
		}

		aq.endSignal.Done()
	}()

	return aq
}

// 停止事件循环
func (aq *actionQueue) StopLoop() ActionQueue {
	aq.Add(nil)
	return aq
}

// 等待退出消息
func (aq *actionQueue) Wait() {
	aq.endSignal.Wait()
}

// 创建默认长度的队列
func NewActionQueue() ActionQueue {
	return &actionQueue{
		ActionPipe: NewActionPipe(),
	}
}
